package com.witframework.web.spring;


import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.context.ApplicationContextException;
import org.springframework.validation.BindException;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.WebUtils;

import com.witframework.core.log.AuditLogger;
import com.witframework.util.DateUtil;


/**
 *
 *
 * @author wuxinyang
 */
abstract public class BaseMultiActionController extends MultiActionController {



	protected Map<String,String> urlMap=new HashMap<String, String>();
	protected UrlPathHelper m_urlPathHelper = new UrlPathHelper();

	protected AuditLogger auditLogger=null;

	protected String taskInstanceId;
	protected String current_account_id;
	protected String current_employee_id;
	protected String current_employee_name;


    public BaseMultiActionController() throws ApplicationContextException {
        super();
    }

    public BaseMultiActionController(Object delegate) throws ApplicationContextException {
        super(delegate);
    }

	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		request.setAttribute("contextPath",request.getContextPath());
		try {
			String methodName = super.getMethodNameResolver()
					.getHandlerMethodName(request);
			ModelAndView mav=invokeNamedMethod(methodName, request, response);
			log(request,mav);
			return mav;
		} catch (NoSuchRequestHandlingMethodException ex) {
			return handleNoSuchRequestHandlingMethod(ex, request, response);
		} catch(ValidationException e){
			ModelAndView mav=new ModelAndView(urlMap.get("input"));
			mav.addObject(e.getCommandObject());
			return mav;
		}
	}
	/**
	 * 数据校验，子类可以通过该方法添加自己的数据校验
	 */
	protected void validate(){
		System.out.println("validation..");
	}
	/**
	 * 默认的日志记录
	 * @param request
	 * @param methodName
	 */
	private void log(HttpServletRequest request,ModelAndView mav){
		if(auditLogger!=null){
			String remoteAddr = request.getRemoteAddr();
			String uri=request.getRequestURI();
			String user="";
			if(request.getUserPrincipal()!=null)
				user=request.getUserPrincipal().getName();
			else if(request.getRemoteUser()!=null)
				user=request.getRemoteUser();
			else
				user="anonymous";			
		}
	}
    /**
     * 初始化binder
     * 设置日期Editor及允许Integer,Double的字符串为空
     */
    @Override
	protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)    {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DateUtil.getDatePattern());
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
        binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class, true));
        binder.registerCustomEditor(Double.class, new CustomNumberEditor(Double.class, true));
        binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
        binder.registerCustomEditor(Long.class, null, new CustomNumberEditor(Long.class, null, true));
        binder.registerCustomEditor(String.class, null,new StringTrimmerEditor(true));
    }

    /**
     * 绑定并校验对象
     * 绑定时的错误将会被忽略，而校验时错误将在BindException中返回
     */
    protected BindException bindObject(HttpServletRequest request, Object command) throws Exception    {
    	request=new ScriptCleanRequestWrapper(request);
    	preBind(request, command);
        ServletRequestDataBinder binder = createBinder(request, command);
        initBinder(request,binder);
        binder.bind(request);
        //BindException errors = new BindException(command, getCommandName(command));
        BindException errors = new BindException(binder.getBindingResult());
        Validator[] validators = getValidators();
        if (validators != null)
        {
            for (int i = 0; i < validators.length; i++)
            {
                if (validators[i].supports(command.getClass()))
                {
                    ValidationUtils.invokeValidator(validators[i], command, errors);
                }
            }
        }
        if(errors.hasErrors())
        	request.setAttribute("errors",errors);
        validate();
        List fieldErrors = (List) request.getSession().getAttribute("fieldErrors");
        if(fieldErrors!=null&&fieldErrors.size()>0){//如果校验出错，转到表单输入视图
        	throw new ValidationException(command);
		}
        return errors;
    }
    protected boolean hasErrors(HttpServletRequest request){
    	if(request.getAttribute("errors")!=null)
    		return true;
    	return false;
    }

    /**
     * 在BindObject的最开始调用,绑定一些不由Binder自动绑定的属性.
     * 通常是需要查询数据库.
     */
    protected void preBind(HttpServletRequest request, Object object) throws Exception  {
        //在子类实现

    }

    /**
     * 回调函数，校验时声明对象的名字,默认为首字母小写的类名
     */
    @Override
	protected String getCommandName(Object command) {
        return StringUtils.uncapitalize(StringUtils.substringAfterLast(command.getClass().getName(), "."));
    }

    /**
     * 将message放入httpSession的messages变量中
     */
    @SuppressWarnings("unchecked")
	protected void setMessage(HttpServletRequest request, String message) {
		if (StringUtils.isNotBlank(message)) {
			List<String> messages = (List<String>) WebUtils.getOrCreateSessionAttribute(request
					.getSession(), "actionMessages", ArrayList.class);
			messages.add(message);
		}
	}
    public void renderHtml(HttpServletResponse response, String content) {
		try {
			response.setContentType("text/html;charset=UTF-8");
			response.getWriter().write(content);

		} catch (IOException e) {
			logger.error(e);
		}
	}
    /**
	 * 直接输出字符串
	 */
	public void renderString(HttpServletResponse response, String content) {
		try {
			response.setContentType("text/plain;charset=UTF-8");
			response.getWriter().write(content);

		} catch (IOException e) {
			logger.error(e);
		}
	}
	public void renderXml(HttpServletResponse response, String content) {
		try {
			response.setContentType("text/xml;charset=UTF-8");
			response.getWriter().write(content);

		} catch (IOException e) {
			logger.error(e);
		}
	}

	/**
	 * Convenience method for getting a i18n key's value. Calling
	 * getMessageSourceAccessor() is used because the RequestContext variable is
	 * not set in unit tests b/c there's no DispatchServlet Request.
	 *
	 * @param msgKey
	 * @return string
	 */
	public String getText(String msgKey) {
		return getMessageSourceAccessor().getMessage(msgKey);
	}

	/**
	 * Convenient method for getting a i18n key's value with a single string
	 * argument.
	 *
	 * @param msgKey
	 * @param arg
	 * @return string
	 */
	public String getText(String msgKey, String arg) {
		return getText(msgKey, new Object[] { arg });
	}

	/**
	 * Convenience method for getting a i18n key's value with arguments.
	 *
	 * @param msgKey
	 * @param args
	 * @return string
	 */
	public String getText(String msgKey, Object[] args) {
		return getMessageSourceAccessor().getMessage(msgKey, args);
	}

    /**
     * 增加validator; 在子类除了Spring注入的validators外,可以用此函数再添加新的validator
     */
    public void addValidator(Validator validator) {
        List<Validator> validators = Arrays.asList(getValidators());
        validators.add(validator);
        setValidators((Validator[]) validators.toArray());
    }

	public void setUrlMap(Map<String, String> urlMap) {
		this.urlMap = urlMap;
	}

	public void setAuditLogger(AuditLogger auditLogger) {
		this.auditLogger = auditLogger;
	}
	public byte[] getFile(HttpServletRequest request,String parameterName) throws IOException{
        MultipartHttpServletRequest multipartRequest = null;
        if(request instanceof MultipartHttpServletRequest){
        	multipartRequest=(MultipartHttpServletRequest)request;
        	MultipartFile multipartFile = multipartRequest.getFile(parameterName);
            return multipartFile.getBytes();

        }
        return null;
	}
	public String uploadToDir(HttpServletRequest request,String uploadDir,String parameterName) throws Exception{
		String fileRelativePath = null;
        MultipartHttpServletRequest multipartRequest = null;
        if(request instanceof MultipartHttpServletRequest){
        	multipartRequest=(MultipartHttpServletRequest)request;
        	MultipartFile multipartFile = multipartRequest.getFile(parameterName);
        	String fileName = multipartFile.getOriginalFilename();
            if (StringUtils.isNotEmpty(fileName)){
                String fileRealPath = getServletContext().getRealPath(uploadDir) + File.separator + fileName;
                File file = new File(fileRealPath);
                multipartFile.transferTo(file);
                fileRelativePath = uploadDir + "/" + fileName;
            }
        }
        return fileRelativePath;
	}
}
final class ScriptCleanRequestWrapper extends HttpServletRequestWrapper{
	private HttpServletRequest request;
	private String exclude_escape_input_fileds=null;
	public ScriptCleanRequestWrapper(HttpServletRequest request) {
		super(request);
		this.request=request;
		exclude_escape_input_fileds=request.getParameter("exclude_escape_input_fileds");
		if(exclude_escape_input_fileds!=null) exclude_escape_input_fileds+=",";
	}
	@Override
	public String getParameter(String name){
		String value= request.getParameter(name);
		if((exclude_escape_input_fileds==null||exclude_escape_input_fileds.indexOf(name)==-1)&&value!=null){
			value=com.witframework.util.StringTools.escapeHTML(value);
		}
		return value;
	}
	@Override
	public String[] getParameterValues(String name){
		String[] values=request.getParameterValues(name);
		if((exclude_escape_input_fileds==null||exclude_escape_input_fileds.indexOf(name)==-1)&&values!=null){
    		for(int i=0;i<values.length;i++){
    			values[i]=com.witframework.util.StringTools.escapeHTML(values[i]);
    		}
		}
		return values;
	}
}
